//文件名: son/son.c
//
//描述: 这个文件实现SON进程
//SON进程首先连接到所有邻居, 然后启动listen_to_neighbor线程, 每个该线程持续接收来自一个邻居的进入报文, 并将该报文转发给SIP进程. 
//然后SON进程等待来自SIP进程的连接. 在与SIP进程建立连接之后, SON进程持续接收来自SIP进程的sendpkt_arg_t结构, 并将接收到的报文发送到重叠网络中.  

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <signal.h>
#include <sys/utsname.h>
#include <assert.h>

#include "../common/constants.h"
#include "../common/pkt.h"
#include "son.h"
#include "../topology/topology.h"
#include "neighbortable.h"
#include "../utils/concurrent.h"
#include "../common/common.h"

//你应该在这个时间段内启动所有重叠网络节点上的SON进程
#define SON_START_DELAY 20
#define START_PORT 6000
#define MAX_CONNECT_RETRY 10
/**************************************************************/
//声明全局变量
/**************************************************************/

//将邻居表声明为一个全局变量 
nbr_entry_t* nt; 
//将与SIP进程之间的TCP连接声明为一个全局变量
int sip_conn; 


/**************************************************************/
//实现重叠网络函数
/**************************************************************/

// 这个线程打开TCP端口CONNECTION_PORT, 等待节点ID比自己大的所有邻居的进入连接,
// 在所有进入连接都建立后, 这个线程终止.
void* waitNbrs(void* arg) 
{
	const int nbr_num=topology_getNbrNum();
	const int nodeID=topology_getMyNodeID();
	in_addr_t nodeIP=topology_getMyNodeIP();

	int wait_num=0;
	for(int i=0;i<nbr_num;i++)
	{
		if(nt[i].nodeID>nodeID)
			wait_num++;
	}

	int server_sockfd=socket(PF_INET,SOCK_STREAM,0);
	struct sockaddr_in sockaddr=create_sockaddr(nodeIP,CONNECTION_PORT);
	bind(server_sockfd,(struct sockaddr*)&sockaddr,sizeof(sockaddr));
	listen(server_sockfd,128);

	int connect_count=0;
	while(connect_count < wait_num)
	{
		struct sockaddr_in nbr_addr;
		socklen_t addr_len=sizeof(nbr_addr);
		int conn=accept(server_sockfd,(struct sockaddr*)&nbr_addr,&addr_len);
		if(conn < 0)
		{
			concurrent_printf_warning("accept failed!\n");
			continue;
		}

		for(int j=0;j < nbr_num;j++)
		{
			if(nt[j].nodeIP==nbr_addr.sin_addr.s_addr)
			{
				nt[j].conn=conn;
				concurrent_printf_reminder("node %d connect me!\n",nt[j].nodeID);
				connect_count++;
				break;
			}
		}
	}
	close(server_sockfd);
	concurrent_printf_reminder("wait neighbours exit!\n");
	return NULL;
}

// 这个函数连接到节点ID比自己小的所有邻居.
// 在所有外出连接都建立后, 返回1, 否则返回-1.
int connectNbrs() 
{
	const int nbr_num=topology_getNbrNum();
	const int nodeID=topology_getMyNodeID();
	u_int16_t port = START_PORT;

	int ret=1;
	for(int i=0;i<nbr_num;i++)
	{
		if(nt[i].nodeID<nodeID)
		{
			int conn=socket(PF_INET,SOCK_STREAM,0);
			struct sockaddr_in self_addr=create_sockaddr(topology_getMyNodeIP(),port);
			bind(conn,(struct sockaddr*)&self_addr,sizeof(self_addr));
			struct sockaddr_in nbr_addr=create_sockaddr(nt[i].nodeIP,CONNECTION_PORT);

			int connect_count=0;
			while(connect(conn,(struct sockaddr*)&nbr_addr,sizeof(nbr_addr))<0 && 
				  ++connect_count < MAX_CONNECT_RETRY)
			{
				concurrent_printf_reminder("retry to connect neighbour%d!\n",nt[i].nodeID);
				srand(time(NULL));
				int connect_retry_interval=rand()%400+100;
				usleep(connect_retry_interval*1000);
			}

			if(connect_count == MAX_CONNECT_RETRY)
			{
				concurrent_printf_warning("retry failed!\n");
				close(conn);
				ret=-1;
			}
			else
			{
				nt[i].conn=conn;
				concurrent_printf_reminder("I connect node %d!\n",nt[i].nodeID);
			}
			port+=100;
		}
	}
	concurrent_printf_reminder("connect neighbours exit!\n");
	return ret;
}

//每个listen_to_neighbor线程持续接收来自一个邻居的报文. 它将接收到的报文转发给SIP进程.
//所有的listen_to_neighbor线程都是在到邻居的TCP连接全部建立之后启动的.
void* listen_to_neighbor(void* arg) {
	pthread_t self_tid=pthread_self();
	pthread_detach(self_tid);

	int nbrIndex=*((int*)arg);
	nbr_entry_t nbr=nt[nbrIndex];
	int conn=nbr.conn;
	while(1)
	{
		sip_pkt_t pkt;
		if(recvpkt(&pkt,conn) < 0)
		{
			concurrent_printf_warning("neighbour:%d connection is broken!\n",nbr.nodeID);
			break;
		}

		int forward_ret=forwardpktToSIP(&pkt,sip_conn);

		if(sip_conn >0 && forward_ret < 0)
		{
			concurrent_printf_warning("SIP connection is broken!\n");
			break;
		}
	}
	close(conn);
	concurrent_printf_reminder("listen_to_neighbor:%d exit!\n",nbr.nodeID);
  	return NULL;
}

//这个函数打开TCP端口SON_PORT, 等待来自本地SIP进程的进入连接. 
//在本地SIP进程连接之后, 这个函数持续接收来自SIP进程的sendpkt_arg_t结构, 并将报文发送到重叠网络中的下一跳. 
//如果下一跳的节点ID为BROADCAST_NODEID, 报文应发送到所有邻居节点.
void waitSIP() 
{
	int server_sockfd=socket(PF_INET,SOCK_STREAM,0);
	struct sockaddr_in server_sockaddr=create_sockaddr(topology_getMyNodeIP(),SON_PORT);
	bind(server_sockfd,(struct sockaddr*)&server_sockaddr,sizeof(server_sockaddr));
	listen(server_sockfd,128);
	
	struct sockaddr_in sip_sockaddr;
	socklen_t sockaddr_len=sizeof(sip_sockaddr);
	sip_conn=accept(server_sockfd,(struct sockaddr*)&sip_sockaddr,&sockaddr_len);
	close(server_sockfd);
	
	while(1)
	{
		sip_pkt_t pkt;
		int next_node;
		if(getpktToSend(&pkt,&next_node,sip_conn) < 0)
		{
			concurrent_printf_warning("SIP connection is broken!\n");
			break;
		}
		int nbr_num=topology_getNbrNum();
		for(int i=0;i<nbr_num;i++)
		{
			if(nt[i].conn != -1 && (next_node == BROADCAST_NODEID || next_node == nt[i].nodeID))
			{
				sip_pkt_t _pkt=pkt;
				if(sendpkt(&_pkt,nt[i].conn) < 0)
				{
					close(nt[i].conn);
					nt[i].conn=-1;
				}
			}
		}
	}
	close(sip_conn);
	concurrent_printf_reminder("waitSIP exit!\n");
}

//这个函数停止重叠网络, 当接收到信号SIGINT时, 该函数被调用.
//它关闭所有的连接, 释放所有动态分配的内存.
void son_stop() {
	close(sip_conn);
	nt_destroy(nt);
	exit(1);
}

int main() {
	//启动重叠网络初始化工作
	printf("Overlay network: Node %d initializing...\n",topology_getMyNodeID());	

	//创建一个邻居表
	nt = nt_create();
	//将sip_conn初始化为-1, 即还未与SIP进程连接.
	sip_conn = -1;
	
	//注册一个信号句柄, 用于终止进程
	signal(SIGINT, son_stop);

	//打印所有邻居
	int nbrNum = topology_getNbrNum();
	int i;
	for(i=0;i<nbrNum;i++) {
		printf("Overlay network: neighbor %d:%d\n",i+1,nt[i].nodeID);
	}

	//启动waitNbrs线程, 等待节点ID比自己大的所有邻居的进入连接
	pthread_t waitNbrs_thread;
	pthread_create(&waitNbrs_thread,NULL,waitNbrs,(void*)0);

	//等待其他节点启动
	//sleep(SON_START_DELAY);
	sleep(10);
	
	//连接到节点ID比自己小的所有邻居
	connectNbrs();

	//等待waitNbrs线程返回
	pthread_join(waitNbrs_thread,NULL);	

	//此时, 所有与邻居之间的连接都建立好了
	
	//创建线程监听所有邻居
	for(i=0;i<nbrNum;i++) {
		int* idx = (int*)malloc(sizeof(int));
		*idx = i;
		pthread_t nbr_listen_thread;
		pthread_create(&nbr_listen_thread,NULL,listen_to_neighbor,(void*)idx);
	}
	printf("Overlay network: node initialized...\n");
	printf("Overlay network: waiting for connection from SIP process...\n");

	//等待来自SIP进程的连接
	waitSIP();
}